/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is Forte for Java, Community Edition. The Initial
* Developer of the Original Code is Sun Microsystems, Inc. Portions
* Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Font;
import java.awt.Color;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentAdapter;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import javax.swing.JLabel;
import javax.swing.JRootPane;
import javax.swing.JLayeredPane;
import javax.swing.Timer;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.UIManager;
import javax.swing.text.JTextComponent;
import javax.swing.text.BadLocationException;
/**
* Support for custom tooltips
*
* @author Miloslav Metelka
* @version 1.00
*/
public class ToolTipSupport extends MouseAdapter
implements MouseMotionListener, ActionListener, PropertyChangeListener,
SettingsChangeListener {
private static final String NO_ROOT_PANE_LOCALE = "no_root_pane"; // NOI18N
private static final String NO_ROOT_PANE_DEFAULT
= "Editor component has no RootPane! Cannot handle tool tips."; // NOI18N
private static final String UI_PREFIX = "ToolTip"; // NOI18N
public static final int INITIAL_DELAY = 1000;
public static final int DISMISS_DELAY = 5000;
ExtUI extUI;
JLabel toolTip;
Timer enterTimer;
Timer exitTimer;
boolean enabled;
protected int lastMouseX;
protected int lastMouseY;
private ComponentAdapter componentL;
public ToolTipSupport(ExtUI extUI) {
this.extUI = extUI;
componentL = new ComponentAdapter() {
public void componentHidden(ComponentEvent evt) {
checkRemoveFromPane();
}
};
enterTimer = new Timer(INITIAL_DELAY, this);
enterTimer.setRepeats(false);
exitTimer = new Timer(DISMISS_DELAY, this);
exitTimer.setRepeats(false);
enabled = true;
Settings.addSettingsChangeListener(this);
extUI.addPropertyChangeListener(this);
}
public final JLabel getToolTip() {
return toolTip;
}
public void settingsChange(SettingsChangeEvent evt) {
Class kitClass = Utilities.getKitClass(extUI.getComponent());
}
public void propertyChange(PropertyChangeEvent evt) {
String propName = evt.getPropertyName();
if (ExtUI.COMPONENT_PROPERTY.equals(propName)) {
if (evt.getNewValue() != null) { // just installed
JTextComponent c = extUI.getComponent();
c.addMouseListener(this);
c.addMouseMotionListener(this);
c.addComponentListener(componentL);
} else { // just deinstalled
JTextComponent c = (JTextComponent)evt.getOldValue();
c.removeMouseListener(this);
c.removeMouseMotionListener(this);
c.removeComponentListener(componentL);
}
}
}
private void checkRemoveFromPane() {
if (toolTip != null) {
JRootPane rp = extUI.getComponent().getRootPane();
if (rp == null) {
return;
}
}
}
private void checkAddToPane() {
if (toolTip != null) {
JRootPane rp = extUI.getComponent().getRootPane();
if (rp == null) {
System.err.println(LocaleSupport.getString(
NO_ROOT_PANE_LOCALE, NO_ROOT_PANE_DEFAULT));
return;
}
// Possibly deinstall the old component from layered pane
JRootPane ttrp = toolTip.getRootPane();
if (ttrp != rp) {
if (ttrp != null) {
ttrp.getLayeredPane().remove(toolTip);
}
rp.getLayeredPane().add(toolTip, JLayeredPane.POPUP_LAYER, 0);
}
}
}
public void setToolTip(JLabel tt) {
if (toolTip == tt) {
return;
}
checkRemoveFromPane();
toolTip = tt;
}
protected JLabel createDefaultToolTip() {
JLabel tt = new JLabel();
Font font = UIManager.getFont(UI_PREFIX + ".font"); // NOI18N
Color backColor = UIManager.getColor(UI_PREFIX + ".background"); // NOI18N
Color foreColor = UIManager.getColor(UI_PREFIX + ".foreground"); // NOI18N
if (font != null) {
tt.setFont(font);
}
if (foreColor != null) {
tt.setForeground(foreColor);
}
if (backColor != null) {
tt.setBackground(backColor);
}
tt.setOpaque(true);
tt.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(tt.getForeground()),
BorderFactory.createEmptyBorder(0, 3, 0, 3)
));
return tt;
}
protected void updateToolTip() {
BaseKit kit = Utilities.getKit(extUI.getComponent());
if (kit != null) {
Action a = kit.getActionByName(BaseKit.buildToolTipAction);
if (a != null) {
a.actionPerformed(new ActionEvent(extUI.getComponent(), 0, "")); // NOI18N
}
}
}
protected void updateToolTipBounds() {
Dimension prefSize = toolTip.getPreferredSize();
Rectangle extBounds = extUI.getExtentBounds();
int x = Math.min(lastMouseX - prefSize.width / 2,
extBounds.x + extBounds.width - prefSize.width);
x = Math.max(x, extBounds.x);
int charHeight = extUI.charHeight;
int y = lastMouseY - 2 * charHeight;
if (y - extBounds.y < charHeight) {
y = lastMouseY + charHeight;
}
toolTip.setBounds(x - extBounds.x, y - extBounds.y, prefSize.width, prefSize.height);
}
protected void setToolTipVisible(boolean visible) {
if (visible) {
updateToolTip();
if (toolTip != null && toolTip.getText() != null
&& toolTip.getText().length() > 0
) {
checkAddToPane();
updateToolTipBounds();
toolTip.setVisible(true);
}
exitTimer.start();
} else { // hide tip
enterTimer.stop();
exitTimer.stop();
if (toolTip != null && toolTip.isVisible()) {
toolTip.setVisible(false);
}
}
}
public boolean isToolTipVisible() {
return (toolTip != null && toolTip.isVisible());
}
public void setToolTipText(String text) {
JLabel tt = getToolTip();
if (tt == null) {
tt = createDefaultToolTip();
setToolTip(tt);
}
tt.setText(text);
updateToolTipBounds();
}
public String getIdentifierUnderCursor() {
String word = null;
try {
JTextComponent component = extUI.getComponent();
BaseTextUI ui = (BaseTextUI)component.getUI();
int pos = ui.viewToModel(component, new Point(lastMouseX, lastMouseY));
if (pos >= 0) {
BaseDocument doc = (BaseDocument)component.getDocument();
int eolPos = Utilities.getRowEnd(doc, pos);
Rectangle eolRect = ui.modelToView(component, eolPos);
int charHeight = Utilities.getExtUI(component).charHeight;
if (lastMouseX <= eolRect.x && lastMouseY <= eolRect.y + charHeight) {
word = Utilities.getIdentifier(doc, pos);
}
}
} catch (BadLocationException e) {
// word will be null
}
return word;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public int getInitialDelay() {
return enterTimer.getDelay();
}
public void setInitialDelay(int delay) {
enterTimer.setDelay(delay);
}
public int getDismissDelay() {
return exitTimer.getDelay();
}
public void setDismissDelay(int delay) {
exitTimer.setDelay(delay);
}
public void actionPerformed(ActionEvent evt) {
if (evt.getSource() == enterTimer) {
if (toolTip != null && toolTip.isVisible()) { // already displayed
return;
}
setToolTipVisible(true);
} else if (evt.getSource() == exitTimer) {
setToolTipVisible(false);
}
}
public void mouseClicked(MouseEvent evt) {
setToolTipVisible(false);
}
public void mousePressed(MouseEvent evt) {
setToolTipVisible(false);
}
public void mouseReleased(MouseEvent evt) {
setToolTipVisible(false);
}
public void mouseEntered(MouseEvent evt) {
// enabled = true;
}
public void mouseExited(MouseEvent evt) {
// enabled = false;
}
public void mouseDragged(MouseEvent evt) {
setToolTipVisible(false);
}
public void mouseMoved(MouseEvent evt) {
setToolTipVisible(false);
if (enabled) {
enterTimer.restart();
}
lastMouseX = evt.getX();
lastMouseY = evt.getY();
}
}
/*
* Log
* 8 Gandalf-post-FCS1.6.1.0 3/8/00 Miloslav Metelka
* 7 Gandalf 1.6 1/16/00 Miloslav Metelka
* 6 Gandalf 1.5 1/13/00 Miloslav Metelka
* 5 Gandalf 1.4 11/14/99 Miloslav Metelka
* 4 Gandalf 1.3 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 3 Gandalf 1.2 8/17/99 Miloslav Metelka
* 2 Gandalf 1.1 7/29/99 Miloslav Metelka
* 1 Gandalf 1.0 7/26/99 Miloslav Metelka
* $
*/